package ke24.ga.extension;

import java.util.*;
import org.jgap.*;
import org.jgap.impl.SwappingMutationOperator;
import org.jgap.util.*;

/**
 * Swaps the genes instead of mutating them. This kind of operator is
 * required by Load Distribution Extension Problem.
 *
 *Now we have two type of package: big and small. 
 *Big packages have dimension of 1*1*2 and small packages still have dimension of 1*1*1.
 *It's not stable to load the big packages vertically, i.e if we consider a big package is made of two small packages, 
 *loading vertically means put one small packages on top of the other. Hence big packages can only load horizontally.
 *We consider a big package as two small packages with half the weight of the big package and whose locations are 
 *next to each other horizontally. 
 *The objective of this operator is to swap between two small packages or between two big packages or between two small packages
 *and a big package so that the constraints "lighter packages lays on top" still satisfies.
 *
 * @author tan
 */
public class LoadDistributionSwappingMutationOperator
    extends SwappingMutationOperator {

  private int m_startOffset = 1;

  /**
   * Constructs a new instance of this operator.<p>
   * Attention: The configuration used is the one set with the static method
   * Genotype.setConfiguration.
   *
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   */
  public LoadDistributionSwappingMutationOperator()
      throws InvalidConfigurationException {
    super();
  }

  /**
   * @param a_config the configuration to use
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.0
   */
  public LoadDistributionSwappingMutationOperator(final Configuration a_config)
      throws InvalidConfigurationException {
    super(a_config);
   
  }

  /**
   * Constructs a new instance of this operator with a specified
   * mutation rate calculator, which results in dynamic mutation being turned
   * on.
   *
   * @param a_config the configuration to use
   * @param a_mutationRateCalculator calculator for dynamic mutation rate
   * computation
   * @throws InvalidConfigurationException
   *
   * @author Klaus Meffert
   * @since 3.0 (previously: without a_config)
   */
  public LoadDistributionSwappingMutationOperator(final Configuration a_config,
                                  final IUniversalRateCalculator
                                  a_mutationRateCalculator)
      throws InvalidConfigurationException {
    super(a_config, a_mutationRateCalculator);
  }

  /**
   * Constructs a new instance of this MutationOperator with the given
   * mutation rate.
   *
   * @param a_config the configuration to use
   * @param a_desiredMutationRate desired rate of mutation, expressed as
   * the denominator of the 1 / X fraction. For example, 1000 would result
   * in 1/1000 genes being mutated on average. A mutation rate of zero disables
   * mutation entirely
   * @throws InvalidConfigurationException
   *
   * @author tan
   */
  public LoadDistributionSwappingMutationOperator(final Configuration a_config,
                                  final int a_desiredMutationRate)
      throws InvalidConfigurationException {
    super(a_config, a_desiredMutationRate);
  }


  /**
   * Operate on the given array of genes. This method is only called
   * when it is already clear that the mutation must occur under the given
   * mutation rate.
   *
   * @param a_generator a random number generator that may be needed to
   * perform a mutation
   * @param a_target_gene an index of gene in the chromosome that will mutate
   * @param a_genes the array of all genes in the chromosome
   * @return the mutated gene array
   *
   * @author Audrius Meskauskas
   * @since 2.0
   */

  protected Gene[] operate(final RandomGenerator a_generator,
          final int a_target_gene, final Gene[] a_genes) {
      // swap this gene with the other one now:
      // mutateGene(genes[j], generator);
      // -------------------------------------
      int other = m_startOffset
              + a_generator.nextInt(a_genes.length - m_startOffset);
      
      int targetPkgId = ((Integer)a_genes[a_target_gene].getAllele()).intValue();
      int otherPkgId = ((Integer)a_genes[other].getAllele()).intValue();
      
      //original implementation from SwappingMutationOperator
      Gene t = a_genes[a_target_gene];
      a_genes[a_target_gene] = a_genes[other];
      a_genes[other] = t;
      if (m_monitorActive) {
          a_genes[a_target_gene].setUniqueIDTemplate(a_genes[other]
                  .getUniqueID(), 1);
          a_genes[other].setUniqueIDTemplate(a_genes[a_target_gene]
                  .getUniqueID(), 1);
      }
     
      if (VariousSizePackage.isBig(targetPkgId) || VariousSizePackage.isBig(otherPkgId)) {
          //at least one of the package is a big package, need to swap the other half as well
          int a_target_gene2 = VariousSizeLoadDistribution.getOtherHalfPosition(a_target_gene);
          int other2 = VariousSizeLoadDistribution.getOtherHalfPosition(other);
          
          Gene t2 = a_genes[a_target_gene2];
          a_genes[a_target_gene2] = a_genes[other2];
          a_genes[other2] = t2;
          if (m_monitorActive) {
              a_genes[a_target_gene2].setUniqueIDTemplate(a_genes[other2]
                      .getUniqueID(), 1);
              a_genes[other2].setUniqueIDTemplate(a_genes[a_target_gene2]
                      .getUniqueID(), 1);
          }
      }            
      
      return a_genes;
  }
  
  protected void reset(int mutationRate) {
      this.setMutationRate(mutationRate);
  }
}

